#include "Sds.hpp"
#include "../Alloc.hpp"

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>

using namespace DBServer::Database;

Sds::Sds()
 : free_len(0), cur_len(0), sds(nullptr)
{}

Sds::Sds(const char* c_string) {
    int len = strlen(c_string);
    sds = Alloc::GetInstance()->New(len);
    // copy
    std::cout << c_string << std::endl;
    memcpy(sds, c_string, len);
    cur_len = len;
    CALCULATE_SIZE(len, free_len);
    free_len -= len;
}

Sds::Sds(const std::string& s)
{
    int len = s.size();
    sds = Alloc::GetInstance()->New(len);
    // copy
    memcpy(sds, s.c_str(), len);
    cur_len = len;
    CALCULATE_SIZE(len, free_len);
    free_len -= len;
}

Sds::Sds(const Sds& _sds)
{
    sds = Alloc::GetInstance()->New(_sds.cur_len);
    // copy
    memcpy(sds, _sds.sds, _sds.cur_len);
    cur_len = _sds.cur_len;
    CALCULATE_SIZE(cur_len, free_len);
    free_len -= cur_len;
}

Sds::Sds(const Sds&& _sds)
{
    // _sds is a rvalue
    free_len = _sds.free_len;
    cur_len = _sds.cur_len;
    // deep copy
    sds = Alloc::GetInstance()->New(cur_len);
    memcpy(sds, _sds.sds, cur_len);
}

Sds::~Sds() {
    if (sds != nullptr)
        Alloc::GetInstance()->Delete(sds);
    sds = nullptr;
}

const Sds& Sds::operator=(const Sds& s)
{
    if (sds != nullptr) Alloc::GetInstance()->Delete(sds);
    sds = Alloc::GetInstance()->New(s.cur_len);
    // copy
    cur_len = s.cur_len;
    memcpy(sds, s.sds, cur_len);
    CALCULATE_SIZE(cur_len, free_len);
    free_len -= cur_len;
    return *this;
}

const Sds& Sds::operator=(const Sds&& s)
{
    Sds new_sds;
    // copy
    new_sds.cur_len = s.cur_len;
    memcpy(new_sds.sds, s.sds, cur_len);
    CALCULATE_SIZE(new_sds.cur_len, new_sds.free_len);
    new_sds.free_len -= new_sds.cur_len;
    return *this;
}

bool
Sds::operator==(const Sds& s) const
{
    if (cur_len != s.cur_len)
        return false;
    
    if (memcmp(sds, s.sds, cur_len) == cur_len)
        return true;
    return false;
}

int
Sds::get_len() {
    return this->cur_len;
}

int
Sds::get_vail() {
    return this->free_len;
}

char*
Sds::get_raw() {
    return this->sds;
}

void
Sds::clear() {
    // lazy free
    free_len += cur_len;
    cur_len = 0;
}

static void dilatation(char **sds, 
                        unsigned int& cur_len, 
                        unsigned int& free_len, 
                        int len)
{
    char *new_sds = Alloc::GetInstance()->New(len);
    memcpy(new_sds, *sds, cur_len);
    // free up old space 
    Alloc::GetInstance()->Delete(*sds);
    *sds = new_sds;
    CALCULATE_SIZE(len, free_len);
    free_len -= cur_len;
}

void
Sds::cat(const char *c_str) {
    // check whether the remaining space can accommodate c characters
    int c_len = strlen(c_str);

    if (c_len > free_len)
    {
        // need dilatation
        dilatation(&sds, cur_len, free_len, cur_len + c_len);
    }

    // splice
    memcpy(sds + cur_len, c_str, c_len);
    cur_len += c_len;
    free_len -= c_len;
}


void
Sds::cat(const Sds& s) {
    // check whether the remaining space can accommodate c charaters
    if (s.cur_len > free_len)
    {
        // dilatation
        dilatation(&sds, cur_len, free_len, s.cur_len + cur_len);
    }

    // splice
    memcpy(sds + cur_len, s.sds, s.cur_len);
    cur_len += s.cur_len;
    free_len -= s.free_len;
}

void
Sds::cat(const std::string& string) {
    // check whether the remaining space can accommodate c characters
    int len = string.size();

    if (len > free_len)
    {
        // need dilatation
        dilatation(&sds, cur_len, free_len, cur_len + len);
    }

    // splice
    memcpy(sds + cur_len, string.c_str(), len);
    cur_len += len;
    free_len -= len;
}

void
Sds::grow_zero(int grow_len) {
    int original_lan = cur_len;
    if (grow_len > free_len)
        dilatation(&sds, cur_len, free_len, cur_len + grow_len);
    else
    {
        free_len -= grow_len;
        cur_len += grow_len;
    }
    memset(sds + original_lan, 0, grow_len);
}

bool
Sds::compare(const Sds& s) {
    return this->operator==(s);
}

#if 0

#include <iostream>

void print(Sds *s)
{
    std::cout << "cur_len ->\t\t" << s->get_len() << "\n";
    std::cout << "free_len ->\t\t" << s->get_vail() << "\n";
    char buff[1024];
    memcpy(buff, s->get_raw(), s->get_len());
    buff[s->get_len()] = '\0';
    std::cout << "data -> \t\t" << buff << "\n";
}

int main() {
    char *c = "this is a c string";
    Sds *s1 = New<Sds, char*>(c);
    print(s1);
    printf("---------------------------\n");
    std::string std_string(c);
    Sds *s2 = New<Sds, std::string>(std_string);
    print(s2);
    printf("---------------------------\n");
    Sds *s3 = New<Sds>(s2);
    print(s3);
    printf("---------------------------\n");
    printf("clear:\n");
    s1->clear();
    print(s1);
    printf("---------------------------\n");
    printf("cat sds: \n");
    s1->cat(*s2);
    print(s1);
    printf("---------------------------\n");
    printf("cat char*: \n");
    s3->cat(c);
    print(s3);
    printf("---------------------------\n");
    printf("cat string: \n");
    s2->cat(std_string);
    print(s2);
    printf("---------------------------\n");
    printf("grow_zero: \n");
    print(s1);
    s1->grow_zero(10);
    print(s1);
    printf("---------------------------\n");
    printf("compare: \n");
    print(s1);
    print(s2);
    std::cout << s1->compare(*s2) << std::endl;
    printf("---------------------------\n");

    
}


#endif